unit BD_UUT;

interface

uses vcl.grids, sysutils, CPort, system.Math, Vcl.StdCtrls, IDGlobal;
(*********CONSTANTS FOR UUT. REFERENCE TESTER SPEC****************************)
const
  //Calibration entry constants
  CAL_UUTACTIVE = $F0; //byte from UUT indicating that it's alive
  CAL_ENTERCAL1 = $00; //1st byte to send UUT to tell it to enter CAL
  CAL_ENTERCAL2 = $01; //2nd byte to send UUT to tell it to enter CAL
  TIM_ENTERCAL = 200;   //100 ms max for UUT to echo cmd.
  CAL_READY = $AA;     //UUT ready- ends most successful exchanges
  //Password exchange constants
  CAL_PW1 = $E3;      //1st byte of Password
  CAL_PW2 = $02;      //2nd byte of Password
  CAL_PW3 = $5F;      //3rd byte of Password
  CAL_PW4 = $16;      //4th byte of Password
  TIM_CALPW = 200;      //PW exchange should compelte within 100mS.
  //LCD test contants
  CAL_LEDON = $AB;      //UUT to turn on LED backlight
  CAL_LEDOFF = $AC;     //UUT to turn off LED backlight
  CAL_LCDON = $AD;      //UUT to turn on all LCD segs
  CAL_LCDOFF = $AE;     //UUT to turn off all LCD segs
  CAL_LCDNORMAL = $AF;  //UUT to have LCD function normally
  TIM_LCD = 100;        //100 mS Max for UUT to echo command
  //AtoD constants
  CAL_SEND_AD = $BA;     //UUT to send a read of A/D data
  TIM_SEND_AD = 500;   //Allow up to 2.5 seconds before timing out
  SZ_AD_VALS = 30;      //30 bytes total
  CAL_SEND_PARAMS = $BC;  //UUT to receive calibration parameters from PC
  TIM_SEND_PARAMS =500;  //each byte in 47 byte message is sent and verified individually
  SZ_SEND_PARAMS = 1;     //bytes exchanged and verified individually
  SZ_TOTAL_PARAMS = 47;   //total number of bytes in parameters array
  //Serial number exchange constants
  CAL_SEND_SN = $BB;     //UUT to send the stored S/N to the PC
  TIM_SEND_SN = 2000;      //allow 2000 ms for SN to come in.
  SZ_SN_VALS = 20;        //20 bytes in SN read from UUT
  CAL_STORE_SN = $BD;    //UUT to receive a S/N from the PC
  TIM_STORE_SN = 20;     //each byte in SN allowed 100 mS to be echoed
  //Standby and exit constants
  CAL_STBY_MODE = $BF;    //UUT to enter standby mode.
  TIM_STBY = 100;         //100 mS for UUT to echo
  CAL_EXIT = $BE;         //UUT to exit calibration mode.
  TIM_EXIT = 200;         //UUT must send cmd + 2 "Readys" in 200 mS or Cal failed.
  //Leak Test Constants
  CAL_LEAK_READ_PER = 100;    //check pressure decay every 100  mS.
  CAL_LEAK_TIME_TOT = 150;    //total of 150 reads for 15 seconds
  CAL_LEAK_RATE_MAX = 2;      //3 ATM leak deacay allowed

(**********CUSTOM CLASSES AND ENUMERATED TYPES FOR BLUE DIAMOND UUT***********)
type TByteArr = array of byte;

//TCalSequence is used in the Rx state machine timer to determine which step in the calibration sequence is
//active
type TCalSequence =(csEnterCal, csSendSN, csReadSN, csCaptureAD, csSendParams, csExitCal);

//TCalType is an enumerated type for easier tracking of the current UUT response flags
type TCalType = (calEnter, calLedOn, calLedOff, calLcdOn, calLcdOff, calLcdNorm);

//TUUTMeasuredVals holds 16 bit values from the UUT with the XOR words stripped off.
type TUUTMeasuredVals = record
  PresAD: word;
  PresADMax: word;
  PresADMin: word;
  Stage1: word;
  Stage1Max: word;
  Stage1Min: word;
end;

//TUUTAtoDVals holds the exact 16 bit values from the UUT including XOR values.
type TUUTAtoDVals = record
  PresAD: word;
  XPresAD: word;
  PresADMax: word;
  XPresADMax: word;
  PresADMin: word;
  XPresADMin: word;
  Stage1: word;
  XStage1: word;
  Stage1Max: word;
  XStage1Max: word;
  Stage1Min: word;
  XStage1Min: word;
  PresAdSamples: byte;
  XPresAdSamples: byte;
  PresStage1Samples: byte;
  XPresStage1Samples: byte;
end;

//TUUTComParams has data members and functions to control the exchange of data
//between the tester and the UUT, based on the parameters being exchanged.
type TUUTComParams = class
  public

    Timeout: Integer;    //populate with the max amount of time for UUT to respond

end;
//TUUT represent the unit under test with applicable data members as well as
//communication flags and controls
type TUUT = class
  public
    UUTReady: Boolean;    //Flag to indicate that UUT has acknowledged being reset
    UUTPendingReset: Boolean;   //Flag to indicate that UUT is awaiting Reset
    RxIdx: Integer;   //track array position when exchanging info with UUT/PC
    RxRplyExpected: byte;  //holds Rply value from SendCmd function
    RxSizeExpected: Integer;   //holds Cnt value fro SendCmd function
    SNCnt: Integer;   //holds the individual components of the serial number
    ADArray: Array[0..27] of byte;
    ADArrayAvg: Array[0..27] of integer;
    WaitForAD: Boolean;   //configures rx state machine to look for AtoD date response
    pRx: pByte;
    ADActive: Boolean;
    adVals: TUUTAtoDVals;
    bParams: array[1..47] of byte; //array to hold the parameters to be sent to the UUT
    WaitForCal: Boolean;  //configures rx state machine to look for CAL response
    CalPWDone: Boolean;  //flag to track status of Password bytes exchange.
    WaitForSN: Boolean;  //configures rx state machine to look for SN response (byte by byte as its configured)
    SNDone: Boolean;     //flag to track status of S/N bytes exchange.
    bSNsent: Array[1..7] of byte;   //track the S/N that was sent to the UUT
    bSNRcv: Array[1..18] of  byte;    //track the S/N received from the UUT
    bSum: byte;           //holds the sum of all 7 bytes of bSNsent
    RcvSNFromUUT: Boolean;  //Config RX State machine to receive SN from unit.
    WaitForSendParams: Boolean;   //make sure correct reply comes from UUT after params are sent.
    WaitForExitCal: Boolean;    //signal state machine to look for exit reply, make sure no errors with CAL
    ZeroVals: TUUTMeasuredVals;  //holds values read from UUT at 0 psi pressure
    MaxVals: TUUTMeasuredVals;   //holds values read from UUT at 440 psi pressure
    RTZVals: TUUTMeasuredVals;  //holds values read from UUT after returing to zero pressure
    A1Calc : Array[1..2] of byte;  //holds endian adjusted A1 calcualted value
    C1Calc: Array[1..2] of byte;   //holds endian adjusted C1 calculated value
    CaptureZero: Boolean;   //Flag to indicate that 15 zero captures are in progress
    CaptureMax: Boolean;    //Flag to indicate that 15 max captures are in progress
    CaptureRtz: Boolean;    //Flag to indicate that 15 RTZ (return to zero) captures are in place
    NumReadsRequired: Integer;  //Specify the number of reads requried
    AppliedPressure: Extended;      //holds the current pressure applied to the UUT
    PressMeasFullPress: Extended;      //saves the actual measured pressure measured at Max Pressure
    PressMeasZeroPress: Extended;      //actual measured pressure at zero  pressure
    PressMeasReturnZeroPress: Extended;   //actual measured pressure after returing to zero pressure
    ExitCal1Done: Boolean;          //2 exit value required, must track
    LeakCnt: Integer;    //track number of 100ms spaced reads. 150 total (15 seconds) will be made.
    LeakTestStartPress: Extended;      //Pressure recoreded when leak test starts
    BattV: Double;            //Battery Voltage
    CalSequence: TCalSequence;   //track the current calibration sequence that the UUT is in.
    function ProcessADData(grid: TStringGrid; col: Integer):string;
    function ExtractWord(pb: pByte): word;
    procedure BuildSN;
    function ProcessSNData(grid: TStringGrid):string;
    procedure InitParams;   //Initialize the bParams array with default values.
    procedure CalcADVals;   //Calculate A1Calc and C1Calc from measured values
    procedure GetAvgVals(var adCont: TUUTMeasuredVals);
    procedure SendCmd(seq: TCalSequence; respTime, respTries: Integer; TxData: array of byte);    //Write a command to the UUT via LJ comport, and start the communications timer.
end;

procedure FillADDataConsts(var grid: TStringGrid);
procedure FillSNConsts(var grid: TStringGrid);

implementation
(*******************************************************************************
 FillADDataConsts: Configure the A/D grid on initial showing of the grid.
*******************************************************************************)
uses BlueDiamondUtilV1, BD_LabJack;

procedure FillADDataConsts(var grid: TStringGrid);
var i: Integer;
begin
  with grid do
    begin
      ColWidths[0] := 120;
      for i := 1 to 3 do
        ColWidths[i] := (width - 120)div 3 - 2;
      DefaultRowHeight := Height div RowCount-1;
      Cells[0,0] := 'PARAMETER';
      Cells[1,0] := 'A/D Zero';
      Cells[2,0] := 'A/D Full';
      Cells[3,0] := 'A/D RTZ';
      Cells[0,1]:= 'A/D Pressure';
      Cells[0,2]:= 'A/D Press Max';
      Cells[0,3]:= 'A/D Press Min';
      Cells[0,4]:= 'Stage1';
      Cells[0,5]:= 'Stage1 Max';
      Cells[0,6]:= 'Stage1 Min';
      Cells[0,7]:= 'A/D Press Samples';
      Cells[0,8]:= 'A/D Stage1 Samples';
    end;
end;
(*******************************************************************************
 FillSNConsts: Configure the SN grid on initial showing of the grid.
*******************************************************************************)
procedure FillSNConsts(var grid: TStringGrid);
begin
  with grid do
    begin
      ColWidths[0] := 80;
      ColWidths[1] := (width-80)-2;
      DefaultRowHeight := Height div RowCount-1;
      Cells[0,0] := 'Tester';
      Cells[0,1] := 'Station';
      Cells[0,2]:= 'Seconds';
      Cells[0,3]:= 'Minutes';
      Cells[0,4]:= 'Hours';
      Cells[0,5]:= 'Day';
      Cells[0,6]:= 'Month';
      Cells[0,7]:= 'Year';
      Cells[0,8]:= 'Sum';
      Cells[0,9]:= 'UUT Version';
    end;
end;
(*******************************************************************************
 ProcessUUTData: Populate data grids based on data recevied from the UUT.
*******************************************************************************)
 function TUUT.ProcessADData(grid: TStringGrid; col: Integer):string;
var
  pW: pWord;
  pB: pByte;
  i: Integer;
  str: string;
begin
  pB := @ADArray[2];
  pW := pWord(pB);
  //extract byte values and copy into record.
  //Current Pressure value
  ADVals.PresAD := ExtractWord(pB);
  grid.Cells[col,1] := IntToStr(ADVals.PresAD);
  //Max Pressure Value
  pB := @ADArray[6];
  ADVals.PresADMax := ExtractWord(pB);
  grid.Cells[col,2] := IntToStr(ADVals.PresADMax);
  //Min Pressure Value
  pB := @ADArray[10];
  ADVals.PresADMin := ExtractWord(pB);
  grid.Cells[col,3] := IntToStr(ADVals.PresADMin);
  //Stage 1
  pB := @ADArray[14];
  ADVals.Stage1 := ExtractWord(pB);
  grid.Cells[col,4] := IntToStr(ADVals.Stage1);
  //Stage 1Max
  pB := @ADArray[18];
  ADVals.Stage1Max := ExtractWord(pB);
  grid.Cells[col,5] := IntToStr(ADVals.Stage1Max);
  //Stage 1 Min
  pB := @ADArray[22];
  ADVals.Stage1Min := ExtractWord(pB);
  grid.Cells[col,6] := IntToStr(ADVals.Stage1);
  //AD Samples Count
  pB := pB + 3;
  ADVals.PresAdSamples := pB^;
  grid.Cells[col,7] := IntToStr(byte(ADVals.PresAdSamples));
  //Stage 1 Samples Count
  pB := pB + 2;
  ADVals.PresStage1Samples := pB^;
  grid.Cells[col,8] := IntToStr(byte(ADVals.PresStage1Samples));
  //Table now updated- clear flag and return string.
  //WaitForAd := False;
  RxIdx := 0;
  for i := 0 to 27 do
    str :=  str + IntToStr(ADArray[i])+' ';
  result := str;
end;

(*******************************************************************************
 ExtractWord: Convert bytes into endian-adjusted word
*******************************************************************************)
function TUUT.ExtractWord(pb: pByte): word;
  begin
    result := pb^;
    result := result shl 8;
    dec(pb);
    result := (result or pb^);
  end;

(*******************************************************************************
 BuildSN: Build SN based on current date and time.
*******************************************************************************)
procedure TUUT.BuildSN;
var
(*
     bSN array will contain the following:
     Byte 1 = Tester # in MSNibble (1-15) / Test Station in LSNibble (1-15)
     Byte 2 = Seconds (0-59)
     Byte 3 = Minutes (0-59)
     Byte 4 = Hours (0-23)
     Byte 5 = Date (1-31)
     Byte 6 = Month (1-12)
     Byte 7 = Year (0-99)
*)
  tmp: TDateTime;  i: Integer;
  yr, mon, day, hr, min, sec, msec: word;
  sum: word;
begin
  tmp := Now;    //get the current date and time.
  DecodeTime(tmp,hr, min, sec, msec);
  DecodeDate(tmp,yr, mon, day);
  yr := yr-2000;     //BD uses 2 digit year, so subtract off 2nd millenia.
  bSNsent[1] := 17;     //Will allocate tester 7, station 7 for this tester.
  bSNsent[2] := byte(sec);
  bSNsent[3] := byte(min);
  bSNsent[4] := byte(hr);
  bSNsent[5] := byte(day);
  bSNsent[6] := byte(mon);
  bSNsent[7] := byte(yr);
  sum := 0;    //initialize
  for i := 1 to 7 do
    sum := sum + bSNsent[i];
  bSum := byte(sum);
end;

(*******************************************************************************
 ProcessSNData: Populate SN grid based on data recevied from the UUT.
*******************************************************************************)
function TUUT.ProcessSNData(grid: TStringGrid):string;
var
  i: Integer;
begin
  grid.Cells[1,0] := IntToStr(((bSNRcv[1] shr 4)and $0F));   //Tester
  grid.Cells[1,1] := IntToStr((bSNRcv[1] and $0F));          //Station
  grid.Cells[1,2] := IntToStr(bSNRcv[3]);     //Seconds
  grid.Cells[1,3] := IntToStr(bSNRcv[5]);     //Minutes
  grid.Cells[1,4] := IntToStr(bSNRcv[7]);     //Hours
  grid.Cells[1,5] := IntToStr(bSNRcv[9]);     //Day
  grid.Cells[1,6] := IntToStr(bSNRcv[11]);    //Month
  grid.Cells[1,7] := IntToStr(bSNRcv[13]);    //Year
  grid.Cells[1,8] := IntToStr(bSNRcv[15]);    //LSB Sum of previous data
  grid.Cells[1,9] := IntToStr(bSNRcv[17]);   //UUT Version
  RcvSNFromUUT := False;     //Done processing received SN
  result := 'SN Done.';
end;

(*******************************************************************************
 InitParams:  Program bParams array with default values
*******************************************************************************)
procedure TUUT.InitParams;
begin
  bParams[1] := $B4;    //A1 Calc LSB    {From Blaine's Code}
  bParams[2] := $03;    //A1 Calc MSB
  bParams[3] := $EF;   //PresA2DMaxCnfg LSB
  bParams[4] := $03;   //PresA2DMaxCnfg MSB
  bParams[5] := $10;   //PresA2DMinCnfg LSB
  bParams[6] := $00;   //PresA2DMinCnfg MSB
  bParams[7] := $07;   //BgnInflCnfg LSB
  bParams[8] := $00;   //BgnInflCnfg MSB
  bParams[9] := $C8;   //C1Calc LSB
  bParams[10] := $FF;  //C1Calc MSB
  bParams[11] := $b9;  //DsplMaxAtmBarCnfg LSB
  bParams[12] := $01;  //DsplMaxAtmBarCnfg MSB
  bParams[13] := $2d;  //DsplMaxPSICnfg LSB
  bParams[14] := $01;  //DsplMaxPSICnfg MSB
  bParams[15] := $f9;  //DsplMinCnfg LSB
  bParams[16] := $ff;  //DsplMinCnfg MSB
  bParams[17] := $0;   //EndInflCnfg LSB
  bParams[18] := $0;   //EndInflCnfg MSB
  bParams[19] := $0;   //GelCnfg LSB
  bParams[20] := $0;   //GelCnfg MSB
  bParams[21] := $D0;  //MaxAllowedInflsCnfg LSB
  bParams[22] := $07;  //MaxAllowedInflsCnfg MSB
  bParams[23] := $f7;  //Stage1A2DMaxAllowedCnfg LSB
  bParams[24] := $03;  //Stage1A2DMaxAllowedCnfg MSB
  bParams[25] := 08;   //Stage1A2DMinAllowedCnfg LSB
  bParams[26] := 00;   //Stage1A2DMinAllowedCnfg MSB
  bParams[27] := $2c;  //OcclusionPresCnfg LSB
  bParams[28] := $01;  //OcclusionPresCnfg MSB
  bParams[29] := $64;  //OcclusionTimeCnfg LSB
  bParams[30] := $00;  //OcclusionTimeCnfg MSB
  bParams[31] := $05;  //AllowUnitsCnfg
  bParams[32] := $01;  //LedDimFreqCnfg
  bParams[33] := $ff;  //LedDimPresResetCnfg
  bParams[34] := $0a;  //LedOffPresResetCnfg
  bParams[35] := $09;  //LedNormalFreqCnfg
  bParams[36] := $96;  //OffTimeCnfg
  bParams[37] := $04;  //OnUnitsCnfg
  bParams[38] := $0a;  //PrsrValCnfg
  bParams[39] := $C8;  //RZWindowCnfg     {from Blaine's code}
  bParams[40] := $05;  //StabilizingPresCnfg
  bParams[41] := $ff;  //LedDimActivationTimeCnfg
  bParams[42] := $0a;  //LedOffActivationTimeCnf
  bParams[43] := $5a;  //AutoOffActivationTimeCnfg
  bParams[44] := $08;  //TotStage1A2DSamplesCnfg
  bParams[45] := $30;  //TotPresSamplesCnfg
  bParams[46] := $03;  //ZWindowCnfg
  bParams[47] := $19;  //PresBlinkDutyCycleCnfg
end;

(*******************************************************************************
 CalcADVals:  Calc the A1 and C1 values from the zero and max pressure measurements.
*******************************************************************************)
procedure TUUT.CalcADVals;
var
  fAcalc, fCcalc: double; wTemp: word;
begin
  frmMain.Memo1.Lines.Add('**************************************************');
  frmMain.Memo1.Lines.Add('Max Press A/D Val: '+IntToHex(MaxVals.PresAD, 0));
  frmMain.Memo1.Lines.Add('Zero Press A/D Val: '+IntToHex(ZeroVals.PresAD,0));
  frmMain.Memo1.Lines.Add('Max Stage1 A/D Val: '+IntToHex(MaxVals.Stage1,0));
  frmMain.Memo1.Lines.Add('Zero Stage1 A/D Val: '+IntToHex(ZeroVals.Stage1,0));
  fAcalc := ((PressMeasFullPress) / (MaxVals.PresAD-ZeroVals.PresAD)) * 1024;
  frmMain.Memo1.Lines.Add('fAcalc Val: '+ FloatToStr(fACalc));
  wTemp := round(fAcalc);
  frmMain.Memo1.Lines.Add('wTemp Val: '+IntToStr(wTemp));
  //format value for correct endianess to send to UUT
  bParams[1] := byte(wTemp and $FF);
  bParams[2] := byte((wTemp shr 8) and $FF);
  fCcalc := PressMeasFullPress -(fAcalc/1024)* MaxVals.PresAD;
  frmMain.Memo1.Lines.Add('fCcalc Val: '+ FloatToStr(fCCalc));
  wTemp := round(fCcalc);
  frmMain.Memo1.Lines.Add('wTemp Val: '+IntToStr(wTemp));
  //format value for correct endianess to send to UUT
  bParams[9] := byte(wTemp and $FF);
  bParams[10] := byte((wTemp shr 8) and $FF);
end;

(*******************************************************************************
GetAvgVals:  Save the max pressure and zero pressure average values.
*******************************************************************************)
procedure TUUT.GetAvgVals(var adCont: TUUTMeasuredVals) ;
var
  pW: pWord;
  pB: pByte;
  i: Integer;
  str: string;
begin
  pB := @ADArray[2];
  pW := pWord(pB);
  //extract byte values and copy into record.
  //Current Pressure value
  ADCont.PresAD := ExtractWord(pB);
  //frmMain.Memo1.Lines.Add('Current Pressure average: '+ IntToStr(ADCont.PresAd));
  //Max Pressure Value
  pB := @ADArray[6];
  ADCont.PresADMax := ExtractWord(pB);
  //frmMain.Memo1.Lines.Add('Max Pressure average: '+ IntToStr(ADCont.PresADMax));
  //Min Pressure Value
  pB := @ADArray[10];
  ADCont.PresADMin := ExtractWord(pB);
  //frmMain.Memo1.Lines.Add('Min Pressure average: '+ IntToStr(ADCont.PresADMin));
  //Stage 1
  pB := @ADArray[14];
  ADCont.Stage1 := ExtractWord(pB);
  //frmMain.Memo1.Lines.Add('Curent Stage1 average: '+ IntToStr(ADCont.Stage1));
  //Stage 1Max
  pB := @ADArray[18];
  ADCont.Stage1Max := ExtractWord(pB);
  //frmMain.Memo1.Lines.Add('Max Stage1 average: '+ IntToStr(ADCont.Stage1Max));
  //Stage 1 Min
  pB := @ADArray[22];
  ADCont.Stage1Min := ExtractWord(pB);
  //frmMain.Memo1.Lines.Add('Min Stage1 average: '+ IntToStr(ADCont.Stage1Min));
end;

(******************************************************************************
  SendCmd(): Send a command to the LJ UART and start comm timer.
*******************************************************************************)
procedure TUUT.SendCmd(seq: TCalSequence; respTime, respTries: Integer; TxData: array of byte);
var i: Integer; str: string;
begin
  //Display the TX data about to be sent to the communications memo.
  str := 'TX->';
  for i := Low(TxData) to High(TxData) do
    begin
      str := str + ByteToHex(TxData[i]) + ' ';
    end;
  frmMain.Memo1.Lines.Add(str);
  CalSequence := seq;           //save param cal sequence to UUT object.
  LJ.RxRespTime := respTime;    //save param respTime to UUT object.
  LJ.RxRespTries := respTries;      //save respTries to UUT object.
  frmMain.ComTmr.Period := respTime;    //check rx buffer each specified response time period
  frmMain.ComTmr.DueTime := respTime;
  LJ.WriteUART(TxData);                //Now send the command from the LJ UART to the UUT.
  frmMain.ComTmr.Enabled := True;    //start the timer.
end;

end.
